﻿function Install-StatMSProductUpdates
{
    <# 
        .SYNOPSIS
            Installs Hotfixes and Cumulative Updates for the Microsoft Products supported by the SharePoint Total Automation Toolkit. The updates to install are listed in the associated configuration file

        .DESCRIPTION
            Part of the Catapult Systems SharePoint Automation Tools (CSAT). This function Installs Hotfixes and Cumulative Updates for the Microsoft Products supported by the SharePoint Total Automation Toolkit. The updates to install are listed in the the associated configuration file as specified from the command line parameters. If the configuration files are stored in the standard path and structure, you have the option of specifying each discrete parameter. Alternatively you can provide the full path to the configuration directory.
 
        .EXAMPLE
            Install-StatMSProductUpdates -Company Contoso -Project Intranet -Release 1.0.0.0 -Environment Development

            If using the default configuration file path and structure, you can provide discrete parameters to each subdirectory.

        .EXAMPLE
            Install-StatMSProductUpdates -ConfigPath 'C:\ProgramData\Catapult SharePoint Automation Tools\Configuration Files\Contoso\Intranet\1.0.0.0\Development'

            Using the -ConfiPath parameter allows you to directly specify the full path to the configuration files that control the process.

        .EXAMPLE
            $myFullPath | Install-StatMSProductUpdates

            This function accepts pipeline input for the -ConfiPath parameter which allows you to directly specify the full path to the configuration files that control the process.

        .PARAMETER Company
            The company subdirectory (e.g. Contoso)
            Part of the overall path to the configuration files that control the installation process

        .PARAMETER Project
            The project subdirectory (e.g. Intranet)
            Part of the overall path to the configuration files that control the installation process

        .PARAMETER Release
            The release subdirectory (e.g. 1.0.0.0)
            Part of the overall path to the configuration files that control the installation process

        .PARAMETER Environment
            The target environment subdirectory (e.g. Development)
            Part of the overall path to the configuration files that control the installation process

        .PARAMETER ConfigPath
            The full path to the configuration files that control the installation process
            (e.g. 'C:\ProgramData\Catapult SharePoint Automation Tools\Configuration Files\Contoso\Intranet\1.0.0.0\Development')

        .PARAMETER Product
            Use this parameter to indicate which product updates to install. Valid choices are 'SP2010', 'SP2013', and 'WacServer'

        .INPUTS
            Install-MSProductUpdates.csv

            Although not technically PowerShell "inputs", this function requires one or more comma separated value (CSV) files to be present at the configuration file path specified in the parameters or at the file path provided on the pipeline input.
            
            -------------------------- FILE 1 --------------------------
            
            Install-MSProductUpdates.csv

            The associated configuration file must have a header row with the following field names:
                Product,Name,InstallationFileName,InstallationFileArguments,InstallationFilePath
        
            This function will process each data row in the file and install the designated. 
            To skip creating an instance of this service application, simply remove all data rows from the file (but leave the header row)
        
            The following are valid values for the associated fields:

                Product
                    The name the product that the update applies to.

                    Required?     true
                    Valid Values  {SP2010, SP2013, WACServer}
                    Example       SP2013

                Name
                    The name the hotfix / cumulative update / service pack.  This is free-form and for documentation & display purposes only.

                    Required?     true
                    Example       KB2767999

                Description
                    The description of the hotfix / cumulative update / service pack.  This is free-form and for documentation & display purposes only.

                    Required?     true
                    Example       March 2013 Public Update

                InstallationFileName
                    The name of the installer filename.

                    Required?     true
                    Example       ubersrvsp2013-kb2767999-fullfile-x64-glb.exe
                
                InstallationFileArguments
                    Custom parameters to pass to the installer.  The default value for this parameter includes arguments that
                    enable a silent unattended installation for the typical Microsoft update package. If the update package
                    specified uses different arguments then use this parameter to override the default values. 
                    Default value "/quiet /passive /norestart"

                    Required?     false
                    Example       /quiet /passive /norestart /log:C:\SetupLog.txt

                InstallationFilePath
                    The path to the install file.

                    Required?     true
                    Example       C:\ProgramData\SharePoint Total Automation Toolkit\Software\06-SharePointUpdates\01-March2013PU

    #>
    [CmdletBinding(DefaultParametersetName="DiscreteValues")]
    param([parameter(ParameterSetName = "DiscreteValues", 
                     Mandatory = $true,
                     HelpMessage = "The company subdirectory (e.g. Contoso). Part of the overall path to the configuration files that control the installation process")]
                     [string]$Company,
	      [parameter(ParameterSetName = "DiscreteValues", 
                     Mandatory = $true,
                     HelpMessage = "The project subdirectory (e.g. Intranet). Part of the overall path to the configuration files that control the installation process")]
                     [string]$Project,
	      [parameter(ParameterSetName = "DiscreteValues", 
                     Mandatory = $true,
                     HelpMessage = "The release subdirectory (e.g. 1.0.0.0). Part of the overall path to the configuration files that control the installation process")]
                     [string]$Release,
	      [parameter(ParameterSetName = "DiscreteValues", 
                     Mandatory = $true,
                     HelpMessage = "The target environment subdirectory (e.g. Development). Part of the overall path to the configuration files that control the installation process")]
                     [string]$Environment,
          [parameter(ParameterSetName = "DiscreteValues", 
                     Mandatory = $true,
                     HelpMessage = "Use this parameter to indicate which product updates to install. Valid choices are 'SP2010', 'SP2013', and 'WacServer'")]
          [parameter(ParameterSetName = "FullPath", 
                     Mandatory = $true,
                     HelpMessage = "Use this parameter to indicate which product updates to install. Valid choices are 'SP2010', 'SP2013', and 'WacServer'")]
                     [ValidateSet ("SP2010","SP2013","WacServer")]
                     [string]$Product,
	      [parameter(ParameterSetName = "FullPath", 
                     Mandatory = $true,
                     ValueFromPipeline=$true,
                     ValueFromPipelineByPropertyName=$true, 
                     HelpMessage = "The full path to the configuration files that control the installation process. (e.g. 'C:\ProgramData\Catapult SharePoint Automation Tools\Configuration Files\Contoso\Intranet\1.0.0.0\Development'")]
                     [string]$ConfigPath)

    switch ($PSCmdlet.ParameterSetName)
    {
        "DiscreteValues"
        {
            $ConfigPath = $env:ProgramData + "\Catapult SharePoint Automation Tools\Configuration Files\" + $Company + "\" + $Project + "\" + $Release + "\" + $Environment
        }    
        "FullPath"
        {
        }
    }

    # Version Compatibility Check Variable
    $firstCompatibleVersion = "0.2.0.0"

    # Conduct Version Compatibility Check
    if ((ConfigVersionIsCompatible $firstCompatibleVersion $ConfigPath))
    {
        # Initialize Progress Variables
        [int]$progressNumPhases = 1
        [int]$progressNumPhasesComplete = 0
        [int]$progressTotalTasks = 1
        [int]$progressNumTasksComplete = 0
        [int]$progressTotal = 0
        [int]$progressBarId = 100
        $progressActivityName = "Hotfix and Cumulative Update Installation"
        $startTime = [System.DateTime]::Now

        # Setup initial paths
        $settingsFile = $ConfigPath + "\Install-MSProductUpdates.csv"

        Write-Host
        Write-Host $progressActivityName

        $progressCurrentOperation = "Processing the configuration file"
	    Write-Progress -Id $progressBarId -Activity $progressActivityName -Status "Reading Input Files" -PercentComplete $progressTotal -CurrentOperation $progressCurrentOperation
        Write-Verbose "$progressCurrentOperation"

        # Load the values from the settings file that correspond to the Product indicated from the command line
        $settings = Import-Csv $settingsFile | Where-Object {($_.Product -eq $Product)}

        if ($settings -ne $null)
        {
	        # Initialize the progress variables to reflect the number of phases
	        $progressNumPhases = @($settings).Count

	        foreach ($settingRow in $settings)
	        {
				$rebootStatus = Get-PendingReboot
				if ($rebootStatus.RebootPending -eq $true)
				{
					Write-Host "NOTE: A system restart is required. Please restart the system prior to continuing with the installation." -ForegroundColor Yellow
					Write-Host "      When the system restarts please rerun this script until you no longer receive a restart request." -ForegroundColor Yellow
					throw 
				}

                # Initialize the progress variables to reflect the number of tasks for the current phase
                $progressNumTasksComplete = 0
                $progressTotalTasks = 1

                # Initialize object variables
                $setupExeInstance = $null

                # Set the default installation arguments.  Note: If custom arguments are provided these will be overwritten
                $installationFileArguments = "/quiet /passive /norestart"

                # Store the values from the CSV into variables
                $name = $settingRow.Name
                $description = $settingRow.Description
                $installationFileName = $settingRow.InstallationFileName
                if ($settingRow.InstallationFileArguments -ne ""){$installationFileArguments = $settingRow.InstallationFileArguments}
                $installationFilePath = $settingRow.InstallationFilePath

                # Ensure that the path has a trailing slash
                if ($installationFilePath.EndsWith("\") -eq $false)
                {
                    $installationFilePath = $installationFilePath + "\"
                }

                $installationFile = $installationFilePath + $installationFileName

                try
                {
                    #region Task 1 - Launch the installer
                    Write-Host "     Installing $name - $description"
	                $progressCurrentOperation = "Installing $name from $installationFile"
                    Write-Progress -Id $progressBarId -Activity $progressActivityName -Status "Validating installation file" -PercentComplete $progressTotal -CurrentOperation $progressCurrentOperation
                    Write-Verbose "$progressCurrentOperation"

                    # Check if the file exists
                    if ((Test-Path $installationFile) -eq $false)
                    {
                        Write-Host "          The installation file $installationFile was not found" -ForegroundColor Yellow
                        throw (New-Object ApplicationException)
                    }

                    # Launch the Installer
                    $setupExeInstance = [System.Diagnostics.Process]::Start($installationFile, $installationFileArguments)
                    $step = 5
                    $value = 0

                    while (-not ($setupExeInstance.HasExited)){
                        $currentRuntime = [System.DateTime]::Now - $startTime
                        Write-Progress -Id $progressBarId -Activity $progressActivityName -Status ("The current operation has been running for {0}:{1}:{2}" -f $currentRuntime.Hours.ToString("00"), $currentRuntime.Minutes.ToString("00"), $currentRuntime.Seconds.ToString("00")) -PercentComplete ($value+=$step) -CurrentOperation $progressCurrentOperation
     
                        if ($value -ge 100) { $value = 0}

                        Start-Sleep -Seconds 1
                    }

                    if ($setupExeInstance.ExitCode -eq 0)
                    {
                        Write-Host "          Installation Successful"
                    }
                    elseif ($setupExeInstance.ExitCode -eq 3010)
                    {
                        Write-Host "          Installation Successful (Reboot Pending)"
                    }
                    elseif ($setupExeInstance.ExitCode -eq 17025)
                    {
                        Write-Host "          Update already installed"
						throw (New-Object ApplicationException)
                    }
                    else
                    {
						$exitCode = $setupExeInstance.ExitCode
                        Write-Host "          Installation Failed with exit code ($exitCode)" -ForegroundColor Red
                        throw
                    }

	                # Increment the progress variables for the current task
	                $progressNumTasksComplete++
	                $progressTotal = CalculateProgress $progressNumTasksComplete $progressTotalTasks $progressNumPhases $progressNumPhasesComplete

                    #endregion Task 1

                }
                catch [ApplicationException]
                {
                    Write-Host "          Skipping to the next item in the configuration file." -ForegroundColor Yellow
                }
		        catch
		        { 
                    Write-Host

                    # Output the full error text
				    Write-Error $_

                    Write-Host "          Skipping to the next item in the configuration file." -ForegroundColor Yellow
		        }
		        finally
		        {
	                # Increment the Phase Completion Counter
	                $progressNumPhasesComplete++
		        }
            
            } # End for each on settings file

        } # End settings file Null Check

		$rebootStatus = Get-PendingReboot
		if ($rebootStatus.RebootPending -eq $true)
		{
			Write-Host "NOTE: A system restart is required. Please restart the system prior to continuing with the installation." -ForegroundColor Yellow
			Write-Host "      When the system restarts please rerun this script until you no longer receive a restart request." -ForegroundColor Yellow
		}

        Write-Progress -Id $progressBarId -Activity $progressActivityName -Status "Finishing" -PercentComplete 100 -CurrentOperation "" -Completed
        Write-Host "$progressActivityName - Finished"
    }
    else
    {
	    Write-Host "     ERROR:" -ForegroundColor Red
	    Write-Host "     This script requires configuration files at version" $firstCompatibleVersion "or higher." -ForegroundColor Red
	    Write-Host "     Use the Get-Help command for a complete list of the configuration files used by this script." -ForegroundColor Red
    }
}